using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;

using Deepcode.Flux.Core.Data;
using Deepcode.Flux.Core.Services.Configuration;
using Deepcode.Flux.Core.Systems.Workflow;
using Deepcode.Flux.Core.Extensions;
using Deepcode.Flux.Core.Systems.Audit;
using Deepcode.Flux.Core.Systems.Audit.Quick;

namespace Deepcode.Flux.Core.Systems.CMS
{
	/// <summary>
	/// ContentManager is the primary class used to
	/// retrieve content from the content object repository.
	/// </summary>
	public class ContentManager : BaseManager
	{
		#region Protected fields
		/// <summary>
		/// Stores whether or not to load content based on version or live
		/// and whether embargo's should be ignored or not. Note this is ignored
		/// when invoking the administrative retrieval functions.
		/// </summary>
		protected ContentManagerMode		Mode = ContentManagerMode.LIVE;
		/// <summary>
		/// Stores whether or not to honour embargo dates on loaded content. Note
		/// this is ignired when invoking the administrative retrieval functions.
		/// </summary>
		protected ContentManagerDateMode	DateMode = ContentManagerDateMode.EMBARGO;
		#endregion
		#region Construction
		/// <summary>
		/// Constructs a content manager class - retrieves live content only
		/// and honours embargo dates on content objects.
		/// </summary>
		public ContentManager() : base()
		{
		}
		/// <summary>
		/// Construct a content manager class, optionally retrieving versioned content.
		/// If versioned is true, embargo dates will be ignored
		/// If versioned is false, embargo dates will be honoured
		/// </summary>
		/// <param name="versioned"></param>
		public ContentManager(bool versioned) : base()
		{
			if (versioned)
			{
				Mode = ContentManagerMode.VERSIONED;
				DateMode = ContentManagerDateMode.IGNORE;
			}
			else
			{
				Mode = ContentManagerMode.LIVE;
				DateMode = ContentManagerDateMode.EMBARGO;
			}
		}
		/// <summary>
		/// Constructs a content manager class, optionally retrieving versioned content
		/// and optionally ignoring embargo dates.
		/// </summary>
		/// <param name="versioned"></param>
		/// <param name="honourdates"></param>
		public ContentManager(bool versioned, bool honourdates) : base()
		{
			if( versioned ) Mode = ContentManagerMode.VERSIONED;
			else Mode = ContentManagerMode.LIVE;

			if( honourdates ) DateMode = ContentManagerDateMode.EMBARGO;
			else DateMode = ContentManagerDateMode.IGNORE;
		}
		/// <summary>
		/// Construct a content manager class using an established connection 
		/// that retrieves live content only and honours embargo dates.
		/// </summary>
		/// <param name="CurrentConnection"></param>
		public ContentManager(SqlConnection CurrentConnection) : base(CurrentConnection) 
		{ 
		}
		/// <summary>
		/// Construct a content manager class using an established connection
		/// that optionally retrieves verioned content and optionally ignores embargo dates.
		/// </summary>
		/// <param name="versioned"></param>
		/// <param name="CurrentConnection"></param>
		public ContentManager(bool versioned, bool honourdates, SqlConnection CurrentConnection) : base(CurrentConnection) 
		{
			if (versioned) Mode = ContentManagerMode.VERSIONED;
			else Mode = ContentManagerMode.LIVE;

			if( honourdates ) DateMode = ContentManagerDateMode.EMBARGO;
			else DateMode = ContentManagerDateMode.IGNORE;
		}
		/// <summary>
		/// Construct a content manager class using an established connection
		/// and running transaction that retrieves live content only and 
		/// honours embargo dates.
		/// </summary>
		/// <param name="CurrentConnection"></param>
		/// <param name="CurrentTransaction"></param>
		public ContentManager(SqlConnection CurrentConnection, SqlTransaction CurrentTransaction) : base(CurrentConnection, CurrentTransaction) 
		{ 
		}
		/// <summary>
		/// Construct a content manager class using an established connection
		/// and running transaction that optionally retrieves versioned content
		/// and optionally ignores embargo dates.
		/// </summary>
		/// <param name="versioned"></param>
		/// <param name="CurrentConnection"></param>
		/// <param name="CurrentTransaction"></param>
		public ContentManager(bool versioned, bool honourdates, SqlConnection CurrentConnection, SqlTransaction CurrentTransaction) : base(CurrentConnection, CurrentTransaction) 
		{
			if (versioned) Mode = ContentManagerMode.VERSIONED;
			else Mode = ContentManagerMode.LIVE;

			if( honourdates ) DateMode = ContentManagerDateMode.EMBARGO;
			else DateMode = ContentManagerDateMode.IGNORE;
		}
		#endregion
		#region Retrieve data - Administratively
		/// <summary>
		/// Administrative function to return an array of content
		/// objects within a specified parent id. Returns versioned
		/// content and ignores embargo dates.
		/// </summary>
		/// <param name="ParentID"></param>
		/// <returns></returns>
		public ContentObject[] AdxGetByParentID(int ParentID, ContentObjectSortOrder Sort, DataSortDirection SortDirection)
		{
			DataTable results = new DataTable();
			EstablishConnection();

			SqlDataAdapter da = this.CreateDataAdapter("coreCmsAdxGetByParentID");
			da.SelectCommand.Parameters.Add( SQLUtility.ParameterIn("@ParentID", SqlDbType.Int, 4, ParentID) );
			da.SelectCommand.Parameters.Add( SQLUtility.ParameterIn("@SortExpression", SqlDbType.NVarChar, 50, Sort.ToString()));
			da.SelectCommand.Parameters.Add( SQLUtility.ParameterIn("@SortDirection", SqlDbType.Int, 4, (int) SortDirection));
			da.Fill(results);
			ReleaseConnection();

			List<ContentObject> list = new List<ContentObject>();
			foreach( DataRow r in results.Rows )
				list.Add( new ContentObject(r));

			return list.ToArray();
		}
		/// <summary>
		/// Administrative function to return a single content object
		/// by it's ID. Returns versioned content and ignores embargo dates
		/// </summary>
		/// <param name="ContentID"></param>
		/// <returns></returns>
		public ContentObject AdxGetByID(int ContentID)
		{
			DataTable result = new DataTable();

			EstablishConnection();
			SqlDataAdapter da = this.CreateDataAdapter("coreCmsAdxGetByID");
			da.SelectCommand.Parameters.Add(SQLUtility.ParameterIn("@id", SqlDbType.Int, 4, ContentID));

			da.Fill(result);
			ReleaseConnection();

			if (result == null) return null;
			if (result.Rows.Count < 1) return null;

			return new ContentObject( result.Rows[0] );
		}
		/// <summary>
		/// Administrative function to return a single content object by
		/// it's ID and a specific version number. Returns versioned content
		/// and ignores embargo dates.
		/// </summary>
		/// <param name="ContentID"></param>
		/// <param name="VersionNumber"></param>
		/// <returns></returns>
		public ContentObject AdxGetByIDAndVersion(int ContentID, int VersionNumber)
		{
			DataTable result = new DataTable();

			EstablishConnection();
			SqlDataAdapter da = this.CreateDataAdapter("coreCmsAdxGetByIDAndVersion");
			da.SelectCommand.Parameters.Add(SQLUtility.ParameterIn("@id", SqlDbType.Int, 4, ContentID));
			da.SelectCommand.Parameters.Add(SQLUtility.ParameterIn("@version", SqlDbType.Int, 4, VersionNumber));

			da.Fill(result);
			ReleaseConnection();

			if (result == null) return null;
			if (result.Rows.Count < 1) return null;

			return new ContentObject(result.Rows[0]);
		}
		/// <summary>
		/// Administrative function to return an array of content objects 
		/// that represent all of the different versions available for one
		/// content ID. Returns versioned content and ignores embargo dates.
		/// </summary>
		/// <param name="ContentID"></param>
		/// <returns></returns>
		public ContentObject[] AdxGetAllVersionsFor(int ContentID)
		{
			DataTable results = new DataTable();
			EstablishConnection();

			SqlDataAdapter da = this.CreateDataAdapter("coreCmsAdxGetVersionsForID");
			da.SelectCommand.Parameters.Add( SQLUtility.ParameterIn("@ContentID", SqlDbType.Int, 4, ContentID) );
			da.Fill( results );
			ReleaseConnection();

			List<ContentObject> list = new List<ContentObject>();
			foreach( DataRow r in results.Rows )
				list.Add( new ContentObject(r));

			return list.ToArray();
		}
		/// <summary>
		/// Administrative function to return an array of content objects
		/// that are checked out to the specified user. Returns versioned 
		/// content and ignores embargo dates.
		/// </summary>
		/// <param name="UserID"></param>
		/// <returns></returns>
		public ContentObject[] AdxGetCheckoutsFor(int UserID)
		{
			DataTable results = new DataTable();
			EstablishConnection();

			SqlDataAdapter da = this.CreateDataAdapter("coreCmsAdxGetCheckoutsFor");
			da.SelectCommand.Parameters.Add(SQLUtility.ParameterIn("@UserID", SqlDbType.Int, 4, UserID));
			da.Fill(results);
			ReleaseConnection();

			List<ContentObject> list = new List<ContentObject>();
			foreach (DataRow r in results.Rows)
				list.Add(new ContentObject(r));

			return list.ToArray();
		}
		/// <summary>
		/// Administrative function to return an array of content objects
		/// that are awaiting submission by the specified user. Returns
		/// versioned content and ignores embargo dates.
		/// </summary>
		/// <param name="UserID"></param>
		/// <returns></returns>
		public ContentObject[] AdxGetWaitingSubmissionFor(int UserID)
		{
			DataTable results = new DataTable();
			EstablishConnection();

			SqlDataAdapter da = this.CreateDataAdapter("coreCmsAdxGetWaitingSubmissionFor");
			da.SelectCommand.Parameters.Add(SQLUtility.ParameterIn("@UserID", SqlDbType.Int, 4, UserID));
			da.Fill(results);
			ReleaseConnection();

			List<ContentObject> list = new List<ContentObject>();
			foreach (DataRow r in results.Rows)
				list.Add(new ContentObject(r));

			return list.ToArray();
		}
		/// <summary>
		/// Administrative function to return an array of content objects that 
		/// have gone beyond their embargo end dates. Only returns objects that are
		/// expired and have an expiry action of delete or move.
		/// </summary>
		/// <returns></returns>
		public ContentObject[] AdxGetExpiredNodes()
		{
			DataTable results = new DataTable();
			EstablishConnection();

			SqlDataAdapter da = this.CreateDataAdapter("coreCmsAdxGetExpired");
			da.Fill(results);
			ReleaseConnection();

			List<ContentObject> list = new List<ContentObject>();
			foreach (DataRow r in results.Rows)
				list.Add(new ContentObject(r));

			return list.ToArray();
		}
		/// <summary>
		/// Administrative function to return a content object based on a parent ID
		/// and the name of a node within that parent group.
		/// </summary>
		/// <param name="ParentID"></param>
		/// <param name="Name"></param>
		/// <returns></returns>
		public ContentObject AdxGetByParentAndName(int ParentID, string Name)
		{
			DataTable result = new DataTable();

			EstablishConnection();
			SqlDataAdapter da = this.CreateDataAdapter("coreCmsAdxGetByParentAndName");
			da.SelectCommand.Parameters.Add(SQLUtility.ParameterIn("@parentid", SqlDbType.Int, 4, ParentID));
			da.SelectCommand.Parameters.Add(SQLUtility.ParameterIn("@nodename", SqlDbType.NVarChar, 100, Name));

			da.Fill(result);
			ReleaseConnection();

			if (result == null) return null;
			if (result.Rows.Count < 1) return null;

			return new ContentObject( result.Rows[0] );
		}
		/// <summary>
		/// Function to search for nodepath within parentid
		/// </summary>
		/// <param name="ParentID"></param>
		/// <param name="Path"></param>
		/// <returns></returns>
		public ContentObject AdxGetByPath(int ParentID, string Path)
		{
			string [] pathpieces = Path.Split('/');
			if( pathpieces.Length < 1 ) return null;

			int CurrentParent = ParentID;
			ContentObject focus = null;
			foreach (string folder in pathpieces)
			{
				if( folder == "" ) continue;
				
				focus = AdxGetByParentAndName( CurrentParent, folder );
				if( focus == null ) return null;

				CurrentParent = focus.PK_ID;
			}
			return focus;
		}
		#endregion
		#region Version Control Methods
		/// <summary>
		/// Checks out the content object specified to the owner specified
		/// </summary>
		/// <param name="ContentID"></param>
		/// <param name="OwnerID"></param>
		public void CheckOut(int ContentID, int OwnerID)
		{
			if(ContentID == -1) return;

			// Clear workflow processing if necessary
			ClearWorkflowFor( ContentID );

			// Checkout the content
			EstablishConnection();
			SqlCommand cmd = this.CreateSqlCommand("coreCmsCheckout");
			cmd.Parameters.Add( SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, ContentID));
			cmd.Parameters.Add( SQLUtility.ParameterIn("@OwnerID", SqlDbType.Int, 4, OwnerID));
			cmd.ExecuteNonQuery();
			ReleaseConnection();
		}
		/// <summary>
		/// Checks the content id, optionally adding specified version notes
		/// </summary>
		/// <param name="OwnerID"></param>
		/// <param name="CheckinNotes"></param>
		/// <returns></returns>
		public void CheckIn(int ContentID, int OwnerID, string CheckinNotes)
		{
			if( ContentID == -1 ) return;

			// Clear workflow
			ClearWorkflowFor( ContentID );
			
			// Check it in
			EstablishConnection();
			SqlCommand cmd = this.CreateSqlCommand("coreCmsCheckin");
			cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, ContentID));
			cmd.Parameters.Add(SQLUtility.ParameterIn("@OwnerID", SqlDbType.Int, 4, OwnerID));
			cmd.Parameters.Add(SQLUtility.ParameterIn("@Notes", SqlDbType.Text, 16, CheckinNotes));
			cmd.ExecuteNonQuery();

			ReleaseConnection();
		}
		/// <summary>
		/// Rolls the content back to an earlier version
		/// </summary>
		/// <param name="RollbackVersion"></param>
		/// <returns></returns>
		public void RollbackTo(int ContentID, int RollbackVersion, int OwnerID)
		{
			if( ContentID == -1 ) return;
			
			// Clear Workflow
			ClearWorkflowFor( ContentID );
			
			// Roll it back
			EstablishConnection();
			SqlCommand cmd = this.CreateSqlCommand("coreCmsRollbackTo");
			cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, ContentID));
			cmd.Parameters.Add(SQLUtility.ParameterIn("@VersionNumber", SqlDbType.Int, 4, RollbackVersion));
			cmd.Parameters.Add(SQLUtility.ParameterIn("@OwnerID", SqlDbType.Int, 4, OwnerID));
			cmd.ExecuteNonQuery();
			ReleaseConnection();
		}
		#endregion
		#region Workflow Methods
		/// <summary>
		/// Aborts processing workflow for a particular piece of content
		/// </summary>
		/// <param name="ContentID"></param>
		protected void ClearWorkflowFor(int ContentID)
		{
			WorkflowEngine e = new WorkflowEngine(Constants.CMSWorkflowID);
			e.ClearProcessingFor(ContentID);
		}
		/// <summary>
		/// Submits the content for approval using the workflow definition ID
		/// specified. If definition is -1 the content is automatically
		/// published - it calls PublishVersion automatically.
		/// </summary>
		/// <param name="WorkflowDefinition"></param>
		public void SubmitForApproval(int ContentID, int WorkflowDefinition, int OwnerID)
		{
			if( ContentID == -1 ) return;

			// Load the content
			ContentManager c = new ContentManager(true);
			ContentObject o = c.AdxGetByID( ContentID );
			if( o == null ) return;

			// Clear existing processing
			ClearWorkflowFor( ContentID );

			// Submit it using the workflow definition specified
			EstablishConnection();
			SqlCommand cmd = this.CreateSqlCommand("coreCmsSubmitForApproval");
			cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, ContentID));
			cmd.ExecuteNonQuery();
			ReleaseConnection();

			// Invoke workflow if specified, publish if not
			if (WorkflowDefinition != -1)
			{
				WorkflowEngine e = new WorkflowEngine(Constants.CMSWorkflowID);
				e.BeginProcessing(WorkflowDefinition, ContentID, o.NodeTitle);
			}
			else
			{
				PublishVersion( ContentID, OwnerID );
			}
		}
		/// <summary>
		/// Publishes the current version of the content record
		/// to the live version immediately.
		/// </summary>
		/// <returns></returns>
		public void PublishVersion( int ContentID, int OwnerID )
		{
			if (ContentID == -1) return;

			int VersionCount = -1;
			try
			{
				// First, get the global version count
				AddinTreeNode cmsSettings = AddinTree.Tree.GetTreeNodeNoThrow("/Flux/CMS/Settings/");
				VersionCount = (int) cmsSettings.BuildChildItem("VersionLimit", this);

				// Now override with the one from the type if need be
				ContentObject o = AdxGetByID( ContentID );
				TemplateGallery.ContentType t = o.TypeTemplate;
				if( t.LimitVersions > 0 ) VersionCount = t.LimitVersions;
			}
			catch{ VersionCount = -1; }


			// Publish it
			EstablishConnection();

			SqlCommand cmd = this.CreateSqlCommand("coreCmsPublishVersion");
			cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, ContentID));
			cmd.Parameters.Add(SQLUtility.ParameterIn("@OwningUserID", SqlDbType.Int, 4, OwnerID));
			cmd.Parameters.Add(SQLUtility.ParameterIn("@VersionCount", SqlDbType.Int, 4, VersionCount));
			cmd.ExecuteNonQuery();

			ReleaseConnection();

			// After an item has been published, we should clear down any versions 
			// as per the global version limiter or as per the version limiter of the type just stored.
		}
		#endregion
		#region Manipulations
		/// <summary>
		/// Shuffles the content item up in the ranks
		/// </summary>
		/// <returns></returns>
		public void RankUp( int ContentID )
		{
			if (ContentID == -1) return;

			// Invoke the action
			EstablishConnection();
			SqlCommand cmd = this.CreateSqlCommand("coreCmsRankUp");
			cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, ContentID));
			cmd.ExecuteNonQuery();
			ReleaseConnection();
		}
		/// <summary>
		/// Shuffles the content item down in the ranks
		/// </summary>
		/// <returns></returns>
		public void RankDown( int ContentID )
		{
			if (ContentID == -1) return;

			// Invoke the action
			EstablishConnection();
			SqlCommand cmd = this.CreateSqlCommand("coreCmsRankDown");
			cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, ContentID));
			cmd.ExecuteNonQuery();
			ReleaseConnection();
		}
		/// <summary>
		/// Moves the content between parents
		/// </summary>
		/// <param name="NewParentID"></param>
		/// <returns></returns>
		public void MoveParent(int ContentID, int NewParentID)
		{
			if (ContentID == -1) return;
			try
			{
				// Invoke the action
				EstablishConnection();
				BeginTransaction();

				// Move the parent as required (auto shifts rank etc)
				SqlCommand cmd = this.CreateSqlCommand("coreCmsMoveParent");
				cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, ContentID));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@newparent", SqlDbType.Int, 4, NewParentID));
				cmd.ExecuteNonQuery();

				// Recalculate paths from the new parent ID and below
				RewritePaths(NewParentID);

				EndTransaction(EndTransactionMode.COMMIT);
				ReleaseConnection();
			}
			catch
			{
				try { EndTransaction(EndTransactionMode.ROLLBACK); }
				catch { }

				try { ReleaseConnection(); }
				catch { }
			}
		}
		/// <summary>
		/// Updates a node's nodename attribute to the value passed in and
		/// recurses into children to recalculate any paths.
		/// </summary>
		/// <param name="ContentID"></param>
		/// <param name="NewName"></param>
		public bool SetNodeName(int ContentID, string NewName)
		{
			if( NewName == "" ) throw new Exception("Cannot set node name to an empty string!");
			if( ContentID == -1 ) return true;
			try
			{
				EstablishConnection();
				BeginTransaction();
				SqlCommand cmd = this.CreateSqlCommand("coreCmsChangeName");
				cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, ContentID));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@newname", SqlDbType.NVarChar, 100, NewName));
				cmd.ExecuteNonQuery();

				if (!RewritePaths(ContentID)) throw new Exception("Couldn't re-write paths");

				EndTransaction(EndTransactionMode.COMMIT);
				ReleaseConnection();
				return true;
			}
			catch
			{
				try { EndTransaction(EndTransactionMode.ROLLBACK); }
				catch { }

				try { ReleaseConnection(); }
				catch { }

				return false;
			}
		}
		/// <summary>
		/// Recalculates (and updates) the full NodePath value of all children
		/// within a particular node ID.
		/// </summary>
		/// <param name="ContentID"></param>
		/// <returns></returns>
		public bool RewritePaths(int ContentID)
		{
			try
			{
				EstablishConnection();
				BeginTransaction();

				// load the item
				ContentObject o = AdxGetByID( ContentID );

				// work out the new path
				string NewPath = "";
				if( o != null ) NewPath = o.NodePath + o.NodeName;
				NewPath += "/";

				// load all children
				ContentObject [] ocs = AdxGetByParentID(ContentID, ContentObjectSortOrder.RANK, DataSortDirection.ASCENDING);
				foreach (ContentObject oc in ocs)
				{
					SqlCommand cmd = this.CreateSqlCommand("coreCmsUpdatePath");
					cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, oc.PK_ID));
					cmd.Parameters.Add(SQLUtility.ParameterIn("@newpath", SqlDbType.NVarChar, 4000, NewPath));
					cmd.ExecuteNonQuery();

					if( ! RewritePaths( oc.PK_ID )) throw new Exception("Couldn't recurse rewrite paths");
				}

				EndTransaction(EndTransactionMode.COMMIT);
				ReleaseConnection();
			}
			catch
			{
				try { EndTransaction(EndTransactionMode.ROLLBACK); }
				catch { }

				try { ReleaseConnection(); }
				catch { }
				return false;
			}
			return true;
		}
		#endregion
		#region Save Methods
		/// <summary>
		/// Saves the content record to the database using either the
		/// new content procedure or update current version procedure.
		/// Pass in owner ID to ensure that either the content is
		/// created checked out to the user or that the content is
		/// owned by the user specified.
		/// </summary>
		/// <param name="OwnerID"></param>
		/// <returns></returns>
		public ContentObject Save(ContentObject obj, int OwnerID)
		{
			if( obj.PK_ID == -1 ) return SaveNew( obj, OwnerID );
			else return Update( obj );
		}
		/// <summary>
		/// Saves a new content record to the database, checked out
		/// to the owner specified.
		/// </summary>
		/// <param name="obj"></param>
		/// <param name="OwnerID"></param>
		protected ContentObject SaveNew(ContentObject obj, int OwnerID)
		{
			if( obj.NodeName == "" ) throw new Exception("Cannot save a new content object with an empty NodeName!");
			// Re-calculate this item's NodePath
			obj.RecalculatePath();

			try
			{
				EstablishConnection();
				BeginTransaction();
				SqlCommand cmd = this.CreateSqlCommand("coreCmsCreateNew");

				SqlParameter contentID = SQLUtility.ParameterInOut("@cid", SqlDbType.Int, 4, -1);
				cmd.Parameters.Add(contentID);
				SqlParameter versionID = SQLUtility.ParameterInOut("@vid", SqlDbType.Int, 4, -1);
				cmd.Parameters.Add(versionID);
				cmd.Parameters.Add(SQLUtility.ParameterIn("@FK_ParentID", SqlDbType.Int, 4, obj.FK_ParentID));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@FK_STypeID", SqlDbType.NVarChar, 250, obj.FK_STypeID));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@NodeTitle", SqlDbType.NVarChar, 500, obj.NodeTitle));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@NodeName", SqlDbType.NVarChar, 100, obj.NodeName));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@NodePath", SqlDbType.NVarChar, 4000, obj.NodePath));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@UserIDOwner", SqlDbType.Int, 4, OwnerID));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@HasEmbargoStart", SqlDbType.Bit, 1, obj.HasEmbargoStart));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@PublishStartDT", SqlDbType.DateTime, 8, obj.PublishStartDT));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@HasEmbargoEnd", SqlDbType.Bit, 1, obj.HasEmbargoEnd));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@PublishUntilDT", SqlDbType.DateTime, 8, obj.PublishUntilDT));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@ExpiryActionID", SqlDbType.Int, 4, obj.ExpiryActionID));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@ExpiryActionDataID", SqlDbType.Int, 4, obj.ExpiryActionDataID));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@XMLData", SqlDbType.NText, 16, obj.XMLData));
				cmd.ExecuteNonQuery();

				// Get the new ID
				int ContentID = (int)contentID.Value;

				// Update the data taxonomy selections
				DataTaxonomy.DataTaxonomyTree tree = new Deepcode.Flux.Core.Systems.CMS.DataTaxonomy.DataTaxonomyTree( ThisConnection, ThisTransaction );
				tree.SetForContent( ContentID, obj.DataTaxonomyCategories );

				EndTransaction(EndTransactionMode.COMMIT);
				ReleaseConnection();

				// Return the new record
				ContentManager cms = new ContentManager(true);
				return cms.AdxGetByID(ContentID);
			}
			catch( Exception e )
			{
				try { EndTransaction(EndTransactionMode.ROLLBACK); }
				catch { }

				try { ReleaseConnection(); }
				catch { }

				throw e;
			}
		}
		/// <summary>
		/// Updates the current version record with new information
		/// </summary>
		/// <param name="obj"></param>
		protected ContentObject Update(ContentObject obj)
		{
			try
			{
				EstablishConnection();
				BeginTransaction();

				SqlCommand cmd = this.CreateSqlCommand("coreCmsUpdateVersion");
				cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, obj.PK_ID));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@NodeTitle", SqlDbType.NVarChar, 500, obj.NodeTitle));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@HasEmbargoStart", SqlDbType.Bit, 1, obj.HasEmbargoStart));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@PublishStartDT", SqlDbType.DateTime, 8, obj.PublishStartDT));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@HasEmbargoEnd", SqlDbType.Bit, 1, obj.HasEmbargoEnd));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@PublishUntilDT", SqlDbType.DateTime, 8, obj.PublishUntilDT));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@ExpiryActionID", SqlDbType.Int, 4, obj.ExpiryActionID));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@ExpiryActionDataID", SqlDbType.Int, 4, obj.ExpiryActionDataID));
				cmd.Parameters.Add(SQLUtility.ParameterIn("@XMLData", SqlDbType.NText, 16, obj.XMLData));
				cmd.ExecuteNonQuery();

				// Update the data taxonomy selections
				DataTaxonomy.DataTaxonomyTree tree = new Deepcode.Flux.Core.Systems.CMS.DataTaxonomy.DataTaxonomyTree( ThisConnection, ThisTransaction );
				tree.SetForContent( obj.PK_ID, obj.DataTaxonomyCategories );

				EndTransaction(EndTransactionMode.COMMIT);
				ReleaseConnection();

				// Load the record back to a new object
				ContentManager cms = new ContentManager(true);
				return cms.AdxGetByID(obj.PK_ID);
			}
			catch (Exception e)
			{
				try { EndTransaction(EndTransactionMode.ROLLBACK); }
				catch { }

				try { ReleaseConnection(); }
				catch { }

				throw e;
			}
		}
		#endregion
		#region Delete Content
		/// <summary>
		/// Deletes the content item specified, triggering recursion in a transaction
		/// </summary>
		/// <returns></returns>
		public bool Delete( int ContentID )
		{
			if( ContentID == -1 ) return true;
			try
			{
				// Establish connection and begin the transaction
				EstablishConnection();
				BeginTransaction();

				// Load the object we're deleting
				ContentObject o = this.AdxGetByID(ContentID);
				if (o != null)
				{
					// determine if there are any children and recurse into them
					ContentObject [] children = this.AdxGetByParentID( o.PK_ID, ContentObjectSortOrder.RANK, DataSortDirection.ASCENDING );
					foreach( ContentObject oc in children )
						if( ! Delete( oc.PK_ID )) throw new Exception("Delete failed on content item");

					// Clear any workflow for the object
					ClearWorkflowFor( ContentID );

					// Invoke the deletion
					SqlCommand cmd = this.CreateSqlCommand("coreCmsDelete");
					cmd.Parameters.Add(SQLUtility.ParameterIn("@cid", SqlDbType.Int, 4, ContentID));
					cmd.ExecuteNonQuery();
				}

				EndTransaction(EndTransactionMode.COMMIT);
				ReleaseConnection();
			}
			catch( Exception error )
			{
				try { EndTransaction(EndTransactionMode.ROLLBACK); }
				catch { }
				try { ReleaseConnection(); }
				catch { }

				return false;
			}
			return true;
		}
		#endregion
	}
}
